#include "_public.h"

#define MAXNUMP_    1000           // 最大进程数量
#define SHMKEYP_    0x5095         // 共享内存的key
#define SEMKEYP_    0x5095

struct  st_pinfo
{
    int pid;            // 进程id
    char pname[51];     // 进程名称
    int timeout;        // 超时时间
    time_t atime;       // 最后一次心跳时间
};

class PActive{
private:
    CSEM m_sem;             // 用于给共享内存加锁的信号量ID
    int m_shmid;            // 共享内存ID
    int m_pos;              // 当前进程在共享内存进程组中的位置
    struct st_pinfo *m_shm; // 指向共享内存的地址空间

public:
    PActive();
    bool AddPInfo(const int timeout, const char *pname);    // 把当前进程的心跳信息加入共享内存中
    bool UptTime();                                         // 更新共享内存进程中当前进程的心跳信息
    ~PActive();
};

PActive::PActive(){
    m_shmid = 1;
    m_pos = -1;
    m_shm = 0;
}

bool PActive::AddPInfo(const int timeout, const char *pname){
    if(m_pos != -1){
        return true;
    }

    // 创建/获取共享内存
    if((m_shmid = shmget(SHMKEYP_, MAXNUMP_ * sizeof(struct st_pinfo), 0640|IPC_CREAT)) == -1){
        cout << "shmget(" << MAXNUMP_ << ") failed " << endl;
        return false;
    }
    // 创建/获取信号量
    if((m_sem.init(SEMKEYP_) == false)){
        cout << "m_sem (" << SEMKEYP_ << ") failed." << endl;
        return false;
    }

    // 将共享内存连接到当前进程的地址空间
    struct st_pinfo *m_shm;
    m_shm = (struct st_pinfo *)shmat(m_shmid, 0, 0);

    // 创建当前进程的心跳结构体变量，输入该进程的信息
    struct st_pinfo stpinfo;
    memset(&stpinfo, 0, sizeof(struct st_pinfo));
    stpinfo.pid = getpid();
    STRNCPY(stpinfo.pname, sizeof(stpinfo.pname), pname, 50);
    stpinfo.timeout = timeout;
    stpinfo.atime = time(0);

    // 若共享内存中存在当前进程编号，一定是其它进程残留的数据，当前进程重用该位置
    for(int i = 0; i < MAXNUMP_; ++i){
        if(m_shm[i].pid == stpinfo.pid){
            m_pos = i;
            break;
        }
    } 

    m_sem.P();
    // 在共享内存中找一个空位置，把当前进程的心跳信息存入共享内存中
    if(m_pos == -1){
        for(int i = 0; i < MAXNUMP_; ++i){
            if(m_shm[i].pid == 0){
                // 找到一个空位置
                m_pos = i;
                break;
            }
        }
    }
    
    if(m_pos == -1){
        m_sem.V();
        cout << "shared memory has been all used." << endl;
        return false;
    }
    memcpy(m_shm+m_pos, &stpinfo, sizeof(struct st_pinfo));

    m_sem.V();

    return true;
}

bool PActive::UptTime(){
    // 更新共享内存中该进程的心跳时间
    if(m_pos != -1){
        return false;
    }
    m_shm[m_pos].atime = time(0);

    return true;
}

PActive::~PActive(){
    // 把当前进程从共享内存中移去
    if(m_pos != -1){
        memset(m_shm+m_pos, 0, sizeof(struct st_pinfo));
    }
    

    // 把共享内存从当前进程中分离
    if(m_shm != 0){
        shmdt(m_shm);
    }
}


int main(int argc, char *argv[]){
    if(argc < 2){
        cout << "USING: ./book procname" << endl;
        return 0;
    }

    PActive Active;
    Active.AddPInfo(30, argv[1]);
    while(true){
        Active.UptTime();
        sleep(10);
    }
    
    return 0;
}
